package Chat.System;

import Chat.Donnees.*;
import java.net.*;
import java.net.ServerSocket;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Classe permettant de gérer l'accès au réseau
 * @author Ndanja & Papa Abdou
 *
 */
public class ChatNI implements Runnable {

    private ChatController ctrl;
    private DatagramSocket monsockeUdp;
    private DatagramSocket ecoute;
    private String adrress;
    private Fichier fichier;
    private ServerSocket socketserver;
    private int ports = 10000;
    private int portc = 10000;

    public ChatNI() {
        try {
            monsockeUdp = new DatagramSocket();
            monsockeUdp.setBroadcast(true);

        } catch (SocketException ex) {
            System.out.println("ecchec de la creation de mon socket pour envoyer mon hello" + ex);
        }
    }

    /**
     * Lancement de l'écoute sur le socket réseau
     */
    public void startListenting() {

        (new Thread(this)).start();
    }

    /**
     * Arret de l'écoute
     */
    public void stopListening() {

        ecoute.close();
    }

    /**
     * Envoie du Hello
     * @param c: Contact de l'utilisateur local
     * @param respReq: Paramètre pour définir si le Hello doit etre repondu ou pas.
     * @param adresse: Adresse de destination
     */
    public void sendHello(Contact c, Boolean respReq, String adresse) {

        try {

            Hello lehello = new Hello(c, respReq);
            byte[] tampon = Message.toByteArray(lehello);
            DatagramPacket helloPacket = new DatagramPacket(tampon, tampon.length, InetAddress.getByName(adresse), portc);
            monsockeUdp.send(helloPacket);

        } catch (Exception ex) {

            System.out.println("Send hello Error");

        }
    }

    /**
     * Envoie d'un message
     * @param m: Message à envoyer
     * @param destCont: liste des Contacts de destination
     * @param srcContact: le contact source
     */
    public void sendMsg(String m, ArrayList<Contact> destCont, Contact srcContact) {
        try {
            Chat chatline = new Chat(srcContact, m);
            byte[] tampon = Message.toByteArray(chatline);
            for (int i = 0; i < destCont.size(); i++) {
                adrress = destCont.get(i).getAddress();
                DatagramPacket messagePacket = new DatagramPacket(tampon, tampon.length, InetAddress.getByName(adrress), portc);
                monsockeUdp.send(messagePacket);

            }
        } catch (Exception ex) {
            System.out.println("Send Message Error");
        }
    }

    /**
     * Envoie d'un bye
     * @param c: Contact de l'utilisateur local
     */
    public void sendBye(Contact c) {

        try {

            Bye lebye = new Bye(c);
            byte[] tampon = Message.toByteArray(lebye);
            DatagramPacket byePacket = new DatagramPacket(tampon, tampon.length, InetAddress.getByName("255.255.255.255"), portc);
            monsockeUdp.send(byePacket);

        } catch (Exception ex) {
            System.out.println("Send Bye Error");
        }

    }

    /**
     * Envoie de fichier
     * @param f: Arborescence du fichier
     * @param destCont: liste des Contacts de destination
     * @param srcContact: Contact source
     * @param port: Port de transfert
     * @param taille: taille du fichier
     */
    public void sendFile(String f, ArrayList<Contact> destCont, Contact srcContact, int port, int taille) {

        try {

            Fichier file = new Fichier(srcContact, f, port, taille);
            byte[] tampon = Message.toByteArray(file);

            for (int i = 0; i < destCont.size(); i++) {

                adrress = destCont.get(i).getAddress();
                DatagramPacket messagePacket = new DatagramPacket(tampon, tampon.length, InetAddress.getByName(adrress), portc);
                monsockeUdp.send(messagePacket);

            }

        } catch (Exception ex) {
            System.out.println("Send Error");

        }


    }

    /**
     * Envoie de l'acquitement pour lancer ou pas le transfert de fichier
     * @param ack
     */
    public void sendAck(FichierAck ack) {

        try {

            FichierAck acquitement = ack;
            byte[] tampon = Message.toByteArray(acquitement);
            DatagramPacket ackPacket = new DatagramPacket(tampon, tampon.length, InetAddress.getByName(acquitement.getSource().getAddress()), portc);
            monsockeUdp.send(ackPacket);

        } catch (Exception ex) {
            Logger.getLogger(ChatNI.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * Pour créer une référence entre Le ChatController et le ChatNI
     * @param ctrl
     */
    public void setController(ChatController ctrl) {
        this.ctrl = ctrl;


    }

    /**
     * Thread traitant les flux réçu du réseau
     */
    @Override
    public void run() {
        try {

            ecoute = new DatagramSocket(ports);
            ecoute.setBroadcast(true);
            while (true) {
                try {
                    byte[] buf = new byte[1024];
                    DatagramPacket paquetRecu = new DatagramPacket(buf, buf.length);
                    ecoute.receive(paquetRecu);
                    Message m = Message.getMessage(paquetRecu.getData(), paquetRecu.getOffset(), paquetRecu.getLength());

                    // Action à la reception d'un Hello
                    if (m instanceof Hello) {

                        if (!m.getSource().getAddress().equalsIgnoreCase(ctrl.getLocalAddress())) {

                            ctrl.newContact(m.getSource(), ((Hello) m).isReplyRequired());
                        }
                    }

                    // Action à la reception d'un Bye
                    if (m instanceof Bye) {
                        ctrl.removeContact(m.getSource());
                    }

                    // Action à la reception d'un Message
                    if (m instanceof Chat) {

                        ctrl.newMessage(m.toString(), m.getSource());

                    }

                    // Action à la reception de la notification d'un transfert de nouveau fichier
                    if (m instanceof Fichier) {

                        ctrl.newFile(((Fichier) m).getFileName(), m.getSource(), ((Fichier) m).getPort(), ((Fichier) m).getTaille());

                    }

                    // Action à la reception d'un acquitement
                    if (m instanceof FichierAck) {

                        ctrl.performSendFileAck((FichierAck) m);

                    }

                } catch (Exception ex) {
                }

            }

        } catch (SocketException ex) {
            System.out.println("Socket Error");

        }

    }
}
